// =============================================================
// CDiscoveryProxy
// ---------------------------------------
// Author : Woo-Hyun Kim
// Email : woorung@icu.ac.kr or woorung@nhncorp.com
// Date : Oct. 9, 2007
// =============================================================
#include "CDiscoveryProxy.h"

CDiscoveryProxy::CDiscoveryProxy()
{
	initialize();
}

CDiscoveryProxy::CDiscoveryProxy(char *host, int port)
{
	discovery_ip = host;
	discovery_port = port;

	initialize();
}

CDiscoveryProxy::~CDiscoveryProxy()
{
	finalize();
}

void CDiscoveryProxy::initialize()
{
	discovery_proxy = NULL;
}

void CDiscoveryProxy::finalize()
{
	if(discovery_proxy!=NULL) delete discovery_proxy;
}

struct soap *CDiscoveryProxy::soap()
{
	return (struct soap *)discovery_proxy->soap();
}

int CDiscoveryProxy::connect()
{
	if(discovery_ip==""||discovery_port<0) return -1;

	if(discovery_proxy==NULL) discovery_proxy = new CServiceProxy();

	discovery_proxy->connect((char *)discovery_ip.c_str(), discovery_port);

	return 1;
}

int CDiscoveryProxy::bind(char *host, int port)
{
	discovery_ip = host;
	discovery_port = port;
}

int CDiscoveryProxy::advertise(CServiceRepository *container, int ttl)
{
	if(connect()<0) {
		cerr << "ERROR : cannot connect discovery server (" << discovery_ip << ":" << discovery_port << ")" << endl;
		return -1;
	}

	vector< CSmartTemplate<CObject> > services;
	services = container->repository->readIfExists(NULL, container->repository->size(), services);

	// do not advertise the instances of service in space
	//services = container->space->readIfExists(NULL, container->space->size(), services);

	CObjectArray *pobjs = new CObjectArray(services.size()+1);
	//pobjs->grow(services.size()+1);

	(*pobjs)[0] = new cube__CKeyValue();
	dynamic_cast<cube__CKeyValue *>((*pobjs)[0])->setKey(CUBE_DISCOVERY_ACTION);
	dynamic_cast<cube__CKeyValue *>((*pobjs)[0])->setValue(CUBE_DISCOVERY_REGISTRY);

	for(int i=0;i<services.size();i++) {
		CService *service = dynamic_cast<CService *>(&(*(services[i])));

		(*pobjs)[i+1] = new cube__CServiceInfo();
		dynamic_cast<cube__CServiceInfo *>((*pobjs)[i+1])->set(service->getName(), container->getIP(), container->getPort(), ttl);
		dynamic_cast<cube__CServiceInfo *>((*pobjs)[i+1])->setStatus(container->getCPU(), container->getLoad());

#ifdef DEBUG
		cout << "+ " << service->getName() << "(" << service->instance() << ") on " << container->getIP() << ":" << container->getPort() << endl;
#endif
	}

	cube__CService discovery_service;
        discovery_service.service(CUBE_SERVICE_DISCOVERY);

	struct cube__invokeResponse res;

	if(discovery_proxy->invoke(&discovery_service, pobjs, res)!=SOAP_OK) {
		soap_print_fault(discovery_proxy->soap(), stderr);

		delete (*pobjs)[0];
		for(int i=0;i<services.size();i++) delete (*pobjs)[i+1];
		delete pobjs;

		return -1;
	}

#ifdef DEBUG
	cout << "-------------- " << CUBE_SERVICE_DISCOVERY << " -------------" << endl;
	cout << "response size = " << ((CObjectArray *)res.out)->__size << endl;

	for(int i=0;i<((CObjectArray *)res.out)->__size;i++) {
		cout << "[" << i << "]";
		dynamic_cast<cube__CKeyValue *>(((CObjectArray *)res.out)->__ptr[i])->print();
	}
#endif

	delete (*pobjs)[0];
	for(int i=0;i<services.size();i++) delete (*pobjs)[i+1];
	delete pobjs;

	return SOAP_OK;
}

vector<CServiceInfo> &CDiscoveryProxy::discover(CServiceInfo *key)
{
	if(connect()<0) {
		cerr << "ERROR : cannot connect discovery server (" << discovery_ip << ":" << discovery_port << ")" << endl;
		exit(-1);
	}

	services_info.clear();

	CObjectArray *pobjs = new CObjectArray(2);
	//pobjs->grow(2);

	(*pobjs)[0] = new cube__CKeyValue();
	dynamic_cast<cube__CKeyValue *>((*pobjs)[0])->setKey(CUBE_DISCOVERY_ACTION);
	dynamic_cast<cube__CKeyValue *>((*pobjs)[0])->setValue(CUBE_DISCOVERY_DISCOVERY);

	// no need in discovery service
	// the end of parameters
	//(*pobjs)[1] = NULL;

	if(key!=NULL) {
		(*pobjs)[1] = new cube__CServiceInfo();
		dynamic_cast<cube__CServiceInfo *>((*pobjs)[1])->setName(key->getName());
	}
	else (*pobjs)[1] = NULL;

	cube__CService discovery_service;
        discovery_service.service(CUBE_SERVICE_DISCOVERY);

	struct cube__invokeResponse res;

	if(discovery_proxy->invoke(&discovery_service, pobjs, res)!=SOAP_OK) {
		soap_print_fault(discovery_proxy->soap(), stderr);
		
		delete (*pobjs)[0];
		if((*pobjs)[1]!=NULL) delete (*pobjs)[1];
		delete pobjs;

		return services_info;
	}
	
#ifdef DEBUG
	cout << "-------------- " << CUBE_SERVICE_DISCOVERY << " -------------" << endl;
	cout << "response size = " << ((CObjectArray *)res.out)->__size << endl;
#endif

	for(int i=0;i<((CObjectArray *)res.out)->__size;i++) {
		cube__CServiceInfo *found = dynamic_cast<cube__CServiceInfo *>(((CObjectArray *)res.out)->__ptr[i]);

#ifdef DEBUG
		cout << "[" << i << "] ----------------------------------" << endl;
		cout << "service name = " << found->getName() << endl;
		cout << "host ip = " << found->getIP() << endl;
		cout << "host port = " << found->getPort() << endl;
		cout << "cpu = " << found->getCPU() << endl;
		cout << "load = " << found->getLoad() << endl;
#endif

		CServiceInfo found_service(found->getName(), found->getIP(), found->getPort(), MAX_TTL);
		found_service.setStatus(found->getCPU(), found->getLoad());
		services_info.push_back(found_service);
	}

	delete (*pobjs)[0];
	if((*pobjs)[1]!=NULL) delete (*pobjs)[1];
	delete pobjs;

	return services_info;
}

int CDiscoveryProxy::invoke(cube__CService *serviceid, cube__CObject *in, struct cube__invokeResponse &res)
{
	if(connect()<0) {
		cerr << "ERROR : cannot connect discovery server (" << discovery_ip << ":" << discovery_port << ")" << endl;
		exit(-1);
	}

	return discovery_proxy->invoke(serviceid, in, res);
}
